#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include "imbus_internal.h"
#include "im_slist.h"
#include "im_attributes.h"

/************************** Declaration of internal functions ***************/

/****************** Declaration of object and class structure ************/
struct _IMAttrList
{
    IMObject object;

    IMSList *attrs;
    IMSList *attrs_tail;
};

struct _IMAttrListClass
{
    IMObjectClass parent_class;
};

struct _IMAttrIterator
{
    IMAttrList *list;
    IMSList    *prev;
    IMSList    *node;
};

/************************ Internal varable ************************/
static const IMObjectClass    *__im_attr_list_parent_class = 0;
static const IMObjectClass    *__im_attribute_parent_class = 0;
static const IMAttributeClass *__im_attribute_class = 0;

/************* Implementation of internal functions ***************/
static void
__im_attribute_initialize (IMPointer attr)
{
    ((IMAttribute *) attr)->start = 0;
    ((IMAttribute *) attr)->end = 0;
}

static void
__im_attribute_copy (IMPointer dest, IMConstPointer src)
{
    IMAttribute       *dp = IM_ATTRIBUTE (dest);
    const IMAttribute *sp = IM_CONST_ATTRIBUTE (src);

    dp->start = sp->start;
    dp->end   = sp->end;

    /* Call copy method of parent class */
    __im_attribute_parent_class->copy (dest, src);
}

static IMBool
__im_attribute_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMAttribute *ap = IM_CONST_ATTRIBUTE (obj);
    IMStream          *sp = IM_STREAM (stream);

    /* Call parent serialize method first*/
    return __im_attribute_parent_class->serialize (obj, stream) && 
           im_stream_put_uint32 (sp, ap->start) &&
           im_stream_put_uint32 (sp, ap->end);
}

static IMBool
__im_attribute_deserialize (IMPointer obj, IMPointer stream)
{
    IMAttribute *ap = IM_ATTRIBUTE (obj);
    IMStream *sp = IM_STREAM (stream);
    IMUInt32 start;
    IMUInt32 end;

    /* Call parent deserialize method */
    if (!__im_attribute_parent_class->deserialize (obj, stream))
        return FALSE;

    if (!im_stream_get_uint32 (sp, &start) || !im_stream_get_uint32 (sp, &end))
        return FALSE;

    ap->start = start;
    ap->end = end;
    return TRUE;
}

static void
__im_attribute_clear (IMAttribute *attr)
{
    attr->start = 0;
    attr->end = 0;
}

static IMBool
__im_attribute_equal (const IMAttribute *attr1, const IMAttribute *attr2)
{
    return TRUE;
}

static void
__im_attribute_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *) klass;

    __im_attribute_class = (IMAttributeClass *) cp;

    cp->copy        = __im_attribute_copy;
    cp->serialize   = __im_attribute_serialize;
    cp->deserialize = __im_attribute_deserialize;

    ((IMAttributeClass *)__im_attribute_class)->clear = __im_attribute_clear;
    ((IMAttributeClass *)__im_attribute_class)->equal = __im_attribute_equal;

    __im_attribute_parent_class = im_object_class_get_parent (cp);
}

/********************** Attribute Color **********************/
static void
__im_attr_color_initialize (IMPointer attr)
{
    ((IMAttrColor *) attr)->red = 0;
    ((IMAttrColor *) attr)->green = 0;
    ((IMAttrColor *) attr)->blue = 0;
}

static void
__im_attr_color_copy (IMPointer dest, IMConstPointer src)
{
    IMAttrColor         *dp = (IMAttrColor *) dest;
    const IMAttrColor   *sp = (const IMAttrColor *) src;

    dp->red = sp->red;
    dp->green = sp->green;
    dp->blue = sp->blue;

    /* Call copy method of parent class */
    ((const IMObjectClass *) __im_attribute_class)->copy (dest, src);
}

static IMBool
__im_attr_color_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMAttrColor *ap = (const IMAttrColor *) obj;
    IMStream          *sp = IM_STREAM (stream);

    /* Call parent serialize method first*/
    return
        ((const IMObjectClass *) __im_attribute_class)->serialize (obj, stream) &&
        im_stream_put_uint16 (sp, ap->red) &&
        im_stream_put_uint16 (sp, ap->green) &&
        im_stream_put_uint16 (sp, ap->blue);
}

static IMBool
__im_attr_color_deserialize (IMPointer obj, IMPointer stream)
{
    IMAttrColor *ap = (IMAttrColor *) obj;
    IMStream *sp = IM_STREAM (stream);
    IMUInt16 red;
    IMUInt16 green;
    IMUInt16 blue;

    /* Call parent deserialize method */
    if (!((const IMObjectClass *) __im_attribute_class)->deserialize (obj, stream))
        return FALSE;

    if (!im_stream_get_uint16 (sp, &red) ||
        !im_stream_get_uint16 (sp, &green) || !im_stream_get_uint16 (sp, &blue))
        return FALSE;

    ap->red = red;
    ap->green = green;
    ap->blue = blue;
    return TRUE;
}

static void
__im_attr_color_clear (IMAttribute *attr)
{
    IMAttrColor *ap = (IMAttrColor *) attr;

    __im_attribute_class->clear (attr);

    ap->red = ap->green = ap->blue = 0;
}

static IMBool
__im_attr_color_equal (const IMAttribute *attr1, const IMAttribute *attr2)
{
    const IMAttrColor *c1 = (const IMAttrColor *) attr1;
    const IMAttrColor *c2 = (const IMAttrColor *) attr2;

    return c1->red == c2->red && c1->green == c2->green && c1->blue == c2->blue;
}

static void
__im_attr_color_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *) klass;

    cp->copy = __im_attr_color_copy;
    cp->serialize = __im_attr_color_serialize;
    cp->deserialize = __im_attr_color_deserialize;

    ((IMAttributeClass *) klass)->clear = __im_attr_color_clear;
    ((IMAttributeClass *) klass)->equal = __im_attr_color_equal;
}
static inline void
__im_attr_color_get (const IMAttrColor * attr, IMUInt16 *red, IMUInt16 *green, IMUInt16 *blue)
{
    if (red)
        *red = attr->red;
    if (green)
        *green = attr->green;
    if (blue)
        *blue = attr->blue;
}
static inline void
__im_attr_color_set (IMAttrColor * attr, IMUInt16 red, IMUInt16 green, IMUInt16 blue)
{
    attr->red = red;
    attr->green = green;
    attr->blue = blue;
}

/********************** Attribute Int **********************/
static void
__im_attr_int_initialize (IMPointer attr)
{
    ((IMAttrInt *) attr)->value = 0;
}

static void
__im_attr_int_copy (IMPointer dest, IMConstPointer src)
{
    IMAttrInt       *dp = (IMAttrInt *) dest;
    const IMAttrInt *sp = (const IMAttrInt *) src;

    dp->value = sp->value;

    /* Call copy method of parent class */
    ((const IMObjectClass *) __im_attribute_class)->copy (dest, src);
}

static IMBool
__im_attr_int_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMAttrInt *ap = (const IMAttrInt *) obj;
    IMStream        *sp = IM_STREAM (stream);

    /* Call parent serialize method first*/
    return
        ((const IMObjectClass *) __im_attribute_class)->serialize (obj, stream) &&
        im_stream_put_int32 (sp, ap->value);
}

static IMBool
__im_attr_int_deserialize (IMPointer obj, IMPointer stream)
{
    IMAttrInt *ap = (IMAttrInt *) obj;
    IMStream *sp = IM_STREAM (stream);
    IMInt32 value;

    /* Call parent deserialize method */
    if (!((const IMObjectClass *) __im_attribute_class)->deserialize (obj, stream))
        return FALSE;

    if (!im_stream_get_int32 (sp, &value))
        return FALSE;

    ap->value = value;
    return TRUE;
}

static void
__im_attr_int_clear (IMAttribute *attr)
{
    __im_attribute_class->clear (attr);

    ((IMAttrInt *) attr)->value = 0;
}

static IMBool
__im_attr_int_equal (const IMAttribute *attr1, const IMAttribute *attr2)
{
    const IMAttrInt *i1 = (const IMAttrInt *) attr1;
    const IMAttrInt *i2 = (const IMAttrInt *) attr2;

    return i1->value == i2->value;
}

static void
__im_attr_int_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *) klass;

    cp->copy        = __im_attr_int_copy;
    cp->serialize   = __im_attr_int_serialize;
    cp->deserialize = __im_attr_int_deserialize;

    ((IMAttributeClass *) klass)->clear = __im_attr_int_clear;
    ((IMAttributeClass *) klass)->equal = __im_attr_int_equal;
}
static inline IMInt
__im_attr_int_get (const IMAttrInt * attr)
{
    return attr->value;
}
static inline void
__im_attr_int_set (IMAttrInt * attr, IMInt value)
{
    attr->value = value;
}

/********************** Attribute String **********************/
static void
__im_attr_string_initialize (IMPointer attr)
{
    ((IMAttrString *) attr)->str = 0;
    ((IMAttrString *) attr)->nocopy = FALSE;
}
static void
__im_attr_string_finalize (IMPointer attr)
{
    IMAttrString *ap = (IMAttrString *) attr;

    if (!ap->nocopy && ap->str)
        im_free (ap->str);
}
static void
__im_attr_string_copy (IMPointer dest, IMConstPointer src)
{
    IMAttrString        *dp = (IMAttrString *) dest;
    const IMAttrString  *sp = (const IMAttrString *) src;

    if (!dp->nocopy && dp->str)
        im_free (dp->str);

    dp->nocopy = sp->nocopy;
    dp->str = 0;

    if (sp->nocopy)
        dp->str = sp->str;
    else if (sp->str)
        dp->str = strdup (sp->str);

    /* Call copy method of parent class */
    ((const IMObjectClass *) __im_attribute_class)->copy (dest, src);
}
static IMBool
__im_attr_string_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMAttrString  *ap = (const IMAttrString *) obj;
    IMStream            *sp = IM_STREAM (stream);

    /* Call parent serialize method */
    if (!((const IMObjectClass *) __im_attribute_class)->serialize (obj, stream))
        return FALSE;

    if (ap->str)
        return im_stream_put_c_string (sp, ap->str, -1);
    else
        return im_stream_put_c_string (sp, "", -1);
}
static IMBool
__im_attr_string_deserialize (IMPointer obj, IMPointer stream)
{
    IMAttrString *ap = (IMAttrString *) obj;
    IMStream *sp = IM_STREAM (stream);
    IMSize len;

    /* Call parent deserialize method */
    if (!((const IMObjectClass *) __im_attribute_class)->deserialize (obj, stream))
        return FALSE;

    if (im_stream_get_data_type (sp) != IM_TYPE_C_STRING)
        return FALSE;

    len = im_stream_get_data_size (sp);

    if (!ap->nocopy && ap->str)
        im_free (ap->str);

    ap->nocopy = FALSE;
    ap->str = 0;

    if (len) {
        ap->str = im_malloc (len + 1);
        _im_assert (ap->str != 0);
        if (!im_stream_get_c_string (sp, ap->str, len + 1)) {
            im_free (ap->str);
            ap->str = 0;
            return FALSE;
        }
        ap->str[len] = 0;
    } else {
        if (!im_stream_get_c_string (sp, 0, 0))
            return FALSE;
    }

    return TRUE;
}
static void
__im_attr_string_clear (IMAttribute *attr)
{
    IMAttrString *ap = (IMAttrString *) attr;

    __im_attribute_class->clear (attr);

    if (!ap->nocopy && ap->str)
        im_free (ap->str);

    ap->nocopy = FALSE;
    ap->str = 0;
}
static IMBool
__im_attr_string_equal (const IMAttribute *attr1, const IMAttribute *attr2)
{
    const IMAttrString *s1 = (const IMAttrString *) attr1;
    const IMAttrString *s2 = (const IMAttrString *) attr2;

    return strcmp (s1->str, s2->str);
}
static void
__im_attr_string_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *) klass;

    cp->copy        = __im_attr_string_copy;
    cp->serialize   = __im_attr_string_serialize;
    cp->deserialize = __im_attr_string_deserialize;

    ((IMAttributeClass *) klass)->clear = __im_attr_string_clear;
    ((IMAttributeClass *) klass)->equal = __im_attr_string_equal;
}
static inline const IMChar *
__im_attr_string_get (const IMAttrString * attr)
{
    return attr->str;
}
static inline void
__im_attr_string_set (IMAttrString * attr, const IMChar *str)
{
    if (!attr->nocopy && attr->str)
        im_free (attr->str);

    attr->nocopy = FALSE;

    if (str)
        attr->str = strdup (str);
    else
        attr->str = 0;
}
static void
__im_attr_string_set_static (IMAttrString * attr, const IMChar *str)
{
    if (!attr->nocopy && attr->str)
        im_free (attr->str);

    attr->nocopy = TRUE;
    attr->str = (IMChar *) str;
}

/********************** Attribute Object **********************/
static void
__im_attr_object_initialize (IMPointer attr)
{
    ((IMAttrObject *) attr)->object = 0;
}
static void
__im_attr_object_finalize (IMPointer attr)
{
    IMAttrObject *ap = (IMAttrObject *) attr;

    if (ap->object)
        im_object_unref (ap->object);
}
static void
__im_attr_object_copy (IMPointer dest, IMConstPointer src)
{
    IMAttrObject        *dp = (IMAttrObject *) dest;
    const IMAttrObject  *sp = (const IMAttrObject *) src;

    if (dp->object)
        im_object_unref (dp->object);

    dp->object = (sp->object ? im_object_ref (im_object_clone (sp->object)) : 0);

    /* Call copy method of parent class */
    ((const IMObjectClass *) __im_attribute_class)->copy (dest, src);
}
static IMBool
__im_attr_object_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMAttrObject  *ap = (const IMAttrObject *) obj;
    IMStream            *sp = IM_STREAM (stream);

    /* Call parent serialize method */
    if (!((const IMObjectClass *) __im_attribute_class)->serialize (obj, stream))
        return FALSE;

    if (ap->object)
        return im_stream_put_object (sp, ap->object);
    else
        return im_stream_put_void (sp);
}
static IMBool
__im_attr_object_deserialize (IMPointer obj, IMPointer stream)
{
    IMAttrObject *ap = (IMAttrObject *) obj;
    IMStream *sp = IM_STREAM (stream);
    IMType type;

    /* Call parent deserialize method */
    if (!((const IMObjectClass *) __im_attribute_class)->deserialize (obj, stream))
        return FALSE;

    type = im_stream_get_data_type (sp);

    if (!IM_TYPE_IS_OBJECT (type) && type != IM_TYPE_VOID)
        return FALSE;

    if (ap->object)
        im_object_unref (ap->object);

    if (type == IM_TYPE_VOID) {
        ap->object = 0;
        return im_stream_get_void (sp);
    }

    ap->object = im_object_ref (im_object_new (type));
    _im_assert (ap->object);

    if (!im_stream_get_object (sp, ap->object)) {
        im_object_unref (ap->object);
        ap->object = 0;
        return FALSE;
    }

    return TRUE;
}

static void
__im_attr_object_clear (IMAttribute *attr)
{
    IMAttrObject *ap = (IMAttrObject *) attr;

    __im_attribute_class->clear (attr);

    if (ap->object)
        im_object_unref (ap->object);
    ap->object = 0;
}

static IMBool
__im_attr_object_equal (const IMAttribute *attr1, const IMAttribute *attr2)
{
    const IMAttrObject *o1 = (const IMAttrObject *) attr1;
    const IMAttrObject *o2 = (const IMAttrObject *) attr2;

    /* FIXME: no way to compare two object */
    return o1->object == o2->object;
}

static void
__im_attr_object_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *) klass;

    cp->copy        = __im_attr_object_copy;
    cp->serialize   = __im_attr_object_serialize;
    cp->deserialize = __im_attr_object_deserialize;

    ((IMAttributeClass *) klass)->clear = __im_attr_object_clear;
    ((IMAttributeClass *) klass)->equal = __im_attr_object_equal;
}
static inline IMPointer
__im_attr_object_get (const IMAttrObject * attr)
{
    return attr->object;
}
static inline IMPointer
__im_attr_object_dup (const IMAttrObject * attr)
{
    return attr->object ? im_object_clone (attr->object) : 0;
}
static void
__im_attr_object_set (IMAttrObject * attr, IMPointer object)
{
    if (attr->object)
        im_object_unref (attr->object);
    attr->object = object;
    if (attr->object)
        im_object_ref (attr->object);
}

/********************** Attribute List **********************/
static void
__im_attr_list_initialize (IMPointer attrlist)
{
    ((IMAttrList *) attrlist)->attrs = 0;
    ((IMAttrList *) attrlist)->attrs_tail = 0;
}

static void
__im_attr_list_finalize (IMPointer attrlist)
{
    IMAttrList *p = IM_ATTR_LIST (attrlist);
    IMSList *iter;

    iter = p->attrs;
    while (iter) {
        im_object_unref (iter->data);
        iter = im_slist_next (iter);
    }

    im_slist_free (p->attrs);

    p->attrs = 0;
    p->attrs_tail = 0;
}

static void
__im_attr_list_copy (IMPointer dest, IMConstPointer src)
{
    IMAttrList       *dp = IM_ATTR_LIST (dest);
    const IMAttrList *sp = IM_CONST_ATTR_LIST (src);
    const IMSList *iter;
    IMSList *new_attrs;

    iter = sp->attrs;
    new_attrs = 0;

    while (iter) {
        new_attrs = im_slist_prepend (new_attrs, im_object_ref (im_object_clone (iter->data)));
        iter = im_slist_next (iter);
    }

    dp->attrs_tail = new_attrs;
    dp->attrs = im_slist_reverse (new_attrs);

    /* Call copy method of parent class */
    __im_attr_list_parent_class->copy (dest, src);
}

static IMBool
__im_attr_list_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMAttrList *lp = IM_CONST_ATTR_LIST (obj);
    IMStream *sp = IM_STREAM (stream);
    IMSize size;
    const IMSList *iter;

    /* Call parent serialize method */
    if (!__im_attr_list_parent_class->serialize (obj, stream))
        return FALSE;

    size = im_slist_length (lp->attrs);
    if (!im_stream_put_uint32 (sp, size))
        return FALSE;

    iter = lp->attrs;
    while (iter) {
        if (!im_stream_put_object (sp, iter->data))
            return FALSE;
        iter = im_slist_next (iter);
    }
    return TRUE;
}

static IMBool
__im_attr_list_deserialize (IMPointer obj, IMPointer stream)
{
    IMAttrList *lp = IM_ATTR_LIST (obj);
    IMStream *sp = IM_STREAM (stream);
    IMSList *new_attrs = 0;
    IMAttribute *attr;
    IMUInt32 size;
    IMType type;

    /* Call parent deserialize method */
    if (!__im_attr_list_parent_class->deserialize (obj, stream))
        return FALSE;

    if (!im_stream_get_uint32 (sp, &size))
        return FALSE;

    /* clear this attribute list */
    __im_attr_list_finalize (lp);

    while (size) {
        type = im_stream_get_data_type (sp);
        if (!im_type_is_a (type, IM_TYPE_ATTRIBUTE))
            break;

        attr = im_object_ref (im_object_new (type));

        _im_assert (attr != 0);

        if (!im_stream_get_object (sp, attr))
            break;

        new_attrs = im_slist_prepend (new_attrs, attr);

        --size;
    }

    lp->attrs_tail = new_attrs;
    lp->attrs = im_slist_reverse (new_attrs);

    return size == 0;
}

static void
__im_attr_list_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *) klass;

    cp->copy        = __im_attr_list_copy;
    cp->serialize   = __im_attr_list_serialize;
    cp->deserialize = __im_attr_list_deserialize;

    __im_attr_list_parent_class = im_object_class_get_parent (cp);
}

/************* Implementation of public functions ***************/
void
__im_type_register_attributes ()
{
    static IMTypeInfo im_attribute_type_info =
    {
        sizeof (IMAttributeClass),  /**< class_size >**/
        __im_attribute_class_init,  /**< class_init >**/
        0,                          /**< class_finalize >**/

        sizeof (IMAttribute),       /**< instance_size >**/
        __im_attribute_initialize,  /**< instance_init >**/
        0,                          /**< instance_finalize >**/
    };

    static IMTypeInfo im_attr_color_type_info =
    {
        sizeof (IMAttributeClass),  /**< class_size >**/
        __im_attr_color_class_init, /**< class_init >**/
        0,                          /**< class_finalize >**/

        sizeof (IMAttrColor),       /**< instance_size >**/
        __im_attr_color_initialize, /**< instance_init >**/
        0,                          /**< instance_finalize >**/
    };

    static IMTypeInfo im_attr_int_type_info =
    {
        sizeof (IMAttributeClass),  /**< class_size >**/
        __im_attr_int_class_init,   /**< class_init >**/
        0,                          /**< class_finalize >**/

        sizeof (IMAttrInt),         /**< instance_size >**/
        __im_attr_int_initialize,   /**< instance_init >**/
        0,                          /**< instance_finalize >**/
    };

    static IMTypeInfo im_attr_string_type_info =
    {
        sizeof (IMAttributeClass),  /**< class_size >**/
        __im_attr_string_class_init,/**< class_init >**/
        0,                          /**< class_finalize >**/

        sizeof (IMAttrString),      /**< instance_size >**/
        __im_attr_string_initialize,/**< instance_init >**/
        __im_attr_string_finalize,  /**< instance_finalize >**/
    };

    static IMTypeInfo im_attr_object_type_info =
    {
        sizeof (IMAttributeClass),  /**< class_size >**/
        __im_attr_object_class_init,/**< class_init >**/
        0,                          /**< class_finalize >**/

        sizeof (IMAttrObject),      /**< instance_size >**/
        __im_attr_object_initialize,/**< instance_init >**/
        __im_attr_object_finalize,  /**< instance_finalize >**/
    };

    static IMTypeInfo im_attr_void_type_info =
    {
        sizeof (IMAttributeClass),  /**< class_size >**/
        0,                          /**< class_init >**/
        0,                          /**< class_finalize >**/

        sizeof (IMAttribute),       /**< instance_size >**/
        0,                          /**< instance_init >**/
        0,                          /**< instance_finalize >**/
    };


    /* IM_TYPE_ATTRIBUTE */
    __im_type_register_builtin_class (IM_TYPE_OBJECT,
                                      IM_TYPE_ATTRIBUTE,
                                      "Attribute",
                                      &im_attribute_type_info,
                                      FALSE);

    /* IM_TYPE_ATTR_LANGUAGE */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_LANGUAGE,
                                      "AttrLanguage",
                                      &im_attr_string_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_FONT_FAMILY */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_FONT_FAMILY,
                                      "AttrFontFamily",
                                      &im_attr_string_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_FONT_STYLE */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_FONT_STYLE,
                                      "AttrFontStyle",
                                      &im_attr_int_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_FONT_WEIGHT */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_FONT_WEIGHT,
                                      "AttrFontWeight",
                                      &im_attr_int_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_FONT_SIZE */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_FONT_SIZE,
                                      "AttrFontSize",
                                      &im_attr_int_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_FONT_SCALE */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_FONT_SCALE,
                                      "AttrFontScale",
                                      &im_attr_int_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_FOREGROUND */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_FOREGROUND,
                                      "AttrForeground",
                                      &im_attr_color_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_BACKGROUND */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_BACKGROUND,
                                      "AttrBackground",
                                      &im_attr_color_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_UNDERLINE */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_UNDERLINE,
                                      "AttrUnderline",
                                      &im_attr_int_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_UNDERLINE_COLOR */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_UNDERLINE_COLOR,
                                      "AttrUnderlineColor",
                                      &im_attr_color_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_STRIKETHROUGH */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_STRIKETHROUGH,
                                      "AttrStrikethrough",
                                      &im_attr_void_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_STRIKETHROUGH_COLOR */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_STRIKETHROUGH_COLOR,
                                      "AttrStrikethroughColor",
                                      &im_attr_color_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_HIGHLIGHT */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_HIGHLIGHT,
                                      "AttrHighlight",
                                      &im_attr_void_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_REVERSE */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_REVERSE,
                                      "AttrReverse",
                                      &im_attr_void_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_STRING */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_STRING,
                                      "AttrString",
                                      &im_attr_string_type_info,
                                      TRUE);
    /* IM_TYPE_ATTR_OBJECT */
    __im_type_register_builtin_class (IM_TYPE_ATTRIBUTE,
                                      IM_TYPE_ATTR_OBJECT,
                                      "AttrObject",
                                      &im_attr_object_type_info,
                                      TRUE);
}

void
__im_type_register_attr_list ()
{
    static IMTypeInfo im_attr_list_type_info =
    {
        sizeof (IMAttrListClass),  /**< class_size >**/
        __im_attr_list_class_init, /**< class_init >**/
        0,                         /**< class_finalize >**/

        sizeof (IMAttrList),       /**< instance_size >**/
        __im_attr_list_initialize, /**< instance_init >**/
        __im_attr_list_finalize,   /**< instance_finalize >**/
    };

    __im_type_register_builtin_class (IM_TYPE_OBJECT,
                                      IM_TYPE_ATTR_LIST,
                                      "AttrList",
                                      &im_attr_list_type_info,
                                      TRUE);
}


IMAttribute *
im_attribute_new (IMType type)
{
    IMAttribute *attr;

    _im_return_val_if_fail (im_type_is_a (type, IM_TYPE_ATTRIBUTE) && type != IM_TYPE_ATTRIBUTE, 0);

    attr = im_object_new (type);
    _im_assert (attr != 0);

    return attr;
}

void
im_attribute_clear (IMAttribute *attr)
{
    const IMAttributeClass *cp = IM_ATTRIBUTE_GET_CLASS (attr);

    _im_return_if_fail (cp != 0);

    cp->clear (attr);
}

void
im_attribute_set_start (IMAttribute *attr, IMUInt start)
{
    _im_return_if_fail (IM_IS_ATTRIBUTE (attr));

    attr->start = start;

    if (attr->end < start) attr->end = start;
}

void
im_attribute_set_end (IMAttribute *attr, IMUInt end)
{
    _im_return_if_fail (IM_IS_ATTRIBUTE (attr));

    attr->end = end;

    if (end < attr->start) attr->start = end;
}

void
im_attribute_set_length (IMAttribute *attr, IMUInt length)
{
    _im_return_if_fail (IM_IS_ATTRIBUTE (attr));

    attr->end = attr->start + length;
}

void
im_attribute_get_range (const IMAttribute *attr, IMUInt *start, IMUInt *end)
{
    _im_return_if_fail (IM_IS_ATTRIBUTE (attr));
    if (start)
        *start = attr->start;
    if (end)
        *end = attr->end;
}

void
im_attribute_set_range (IMAttribute *attr, IMUInt start, IMUInt end)
{
    _im_return_if_fail (IM_IS_ATTRIBUTE (attr));
    if (start > end)
        IM_SWAP (IMUInt, start, end);
    attr->start = start;
    attr->end = end;
}

IMBool
im_attribute_equal (const IMAttribute *attr1, const IMAttribute *attr2)
{
    const IMAttributeClass *cp;

    _im_return_val_if_fail (IM_IS_ATTRIBUTE (attr1), FALSE);
    _im_return_val_if_fail (IM_IS_ATTRIBUTE (attr2), FALSE);

    if (IM_ATTRIBUTE_TYPE (attr1) != IM_ATTRIBUTE_TYPE (attr2))
        return FALSE;

    cp = IM_ATTRIBUTE_GET_CLASS (attr1);

    return cp->equal (attr1, attr2);
}


IMAttribute *
im_attr_language_new (const IMChar *lang)
{
    IMAttrString *attr = (IMAttrString *) im_attribute_new (IM_TYPE_ATTR_LANGUAGE);
    _im_assert (attr != 0);
    __im_attr_string_set (attr, lang);
    return (IMAttribute *) attr;
}
const IMChar *
im_attr_language_get (const IMAttribute *attr)
{
    _im_return_val_if_fail (IM_IS_ATTR_LANGUAGE (attr), 0);
    return __im_attr_string_get ((const IMAttrString *) attr);
}

void
im_attr_language_set (IMAttribute *attr, const IMChar *lang)
{
    _im_return_if_fail (IM_IS_ATTR_LANGUAGE (attr));
    __im_attr_string_set ((IMAttrString *) attr, lang);
}

void
im_attr_language_set_static (IMAttribute *attr, const IMChar *lang)
{
    _im_return_if_fail (IM_IS_ATTR_LANGUAGE (attr));
    __im_attr_string_set_static ((IMAttrString *) attr, lang);
}

IMAttribute *
im_attr_font_family_new (const IMChar *family)
{
    IMAttrString *attr = (IMAttrString *) im_attribute_new (IM_TYPE_ATTR_FONT_FAMILY);
    _im_assert (attr != 0);
    __im_attr_string_set (attr, family);
    return (IMAttribute *) attr;
}
const IMChar *
im_attr_font_family_get (const IMAttribute *attr)
{
    _im_return_val_if_fail (IM_IS_ATTR_FONT_FAMILY (attr), 0);
    return __im_attr_string_get ((const IMAttrString *) attr);
}

void
im_attr_font_family_set (IMAttribute *attr, const IMChar *family)
{
    _im_return_if_fail (IM_IS_ATTR_FONT_FAMILY (attr));
    __im_attr_string_set ((IMAttrString *) attr, family);
}

void
im_attr_font_family_set_static (IMAttribute *attr, const IMChar *family)
{
    _im_return_if_fail (IM_IS_ATTR_FONT_FAMILY (attr));
    __im_attr_string_set_static ((IMAttrString *) attr, family);
}

IMAttribute *
im_attr_string_new (const IMChar *str)
{
    IMAttrString *attr = (IMAttrString *) im_attribute_new (IM_TYPE_ATTR_STRING);
    _im_assert (attr != 0);
    __im_attr_string_set (attr, str);
    return (IMAttribute *) attr;
}
const IMChar *
im_attr_string_get (const IMAttribute *attr)
{
    _im_return_val_if_fail (IM_IS_ATTR_STRING (attr), 0);
    return __im_attr_string_get ((const IMAttrString *) attr);
}

void
im_attr_string_set (IMAttribute *attr, const IMChar *str)
{
    _im_return_if_fail (IM_IS_ATTR_STRING (attr));
    __im_attr_string_set ((IMAttrString *) attr, str);
}

void
im_attr_string_set_static (IMAttribute *attr, const IMChar *str)
{
    _im_return_if_fail (IM_IS_ATTR_STRING (attr));
    __im_attr_string_set_static ((IMAttrString *) attr, str);
}

IMAttribute *
im_attr_font_style_new (IMAttrFontStyle style)
{
    IMAttrInt *attr = (IMAttrInt *) im_attribute_new (IM_TYPE_ATTR_FONT_STYLE);
    _im_assert (attr != 0);
    __im_attr_int_set (attr, style);
    return (IMAttribute *) attr;
}

IMAttrFontStyle
im_attr_font_style_get (const IMAttribute *attr)
{
    _im_return_val_if_fail (IM_IS_ATTR_FONT_STYLE (attr), IM_ATTR_FONT_STYLE_NORMAL);
    return (IMAttrFontStyle) __im_attr_int_get ((const IMAttrInt *) attr);
}

void
im_attr_font_style_set (IMAttribute *attr, IMAttrFontStyle style)
{
    _im_return_if_fail (IM_IS_ATTR_FONT_STYLE (attr));
    __im_attr_int_set ((IMAttrInt *) attr, style);
}

IMAttribute *
im_attr_font_weight_new (IMAttrFontWeight weight)
{
    IMAttrInt *attr = (IMAttrInt *) im_attribute_new (IM_TYPE_ATTR_FONT_WEIGHT);
    _im_assert (attr != 0);
    __im_attr_int_set (attr, weight);
    return (IMAttribute *) attr;
}

IMAttrFontWeight
im_attr_font_weight_get (const IMAttribute *attr)
{
    _im_return_val_if_fail (IM_IS_ATTR_FONT_WEIGHT (attr), IM_ATTR_FONT_WEIGHT_NORMAL);
    return (IMAttrFontWeight) __im_attr_int_get ((const IMAttrInt *) attr);
}

void
im_attr_font_weight_set (IMAttribute *attr, IMAttrFontWeight weight)
{
    _im_return_if_fail (IM_IS_ATTR_FONT_WEIGHT (attr));
    __im_attr_int_set ((IMAttrInt *) attr, weight);
}

IMAttribute *
im_attr_font_size_new (IMInt size)
{
    IMAttrInt *attr = (IMAttrInt *) im_attribute_new (IM_TYPE_ATTR_FONT_SIZE);
    _im_assert (attr != 0);
    __im_attr_int_set (attr, size);
    return (IMAttribute *) attr;
}

IMInt
im_attr_font_size_get (const IMAttribute *attr)
{
    _im_return_val_if_fail (IM_IS_ATTR_FONT_SIZE (attr), 0);
    return __im_attr_int_get ((const IMAttrInt *) attr);
}

void
im_attr_font_size_set (IMAttribute *attr, IMInt size)
{
    _im_return_if_fail (IM_IS_ATTR_FONT_SIZE (attr));
    __im_attr_int_set ((IMAttrInt *) attr, size);
}

IMAttribute *
im_attr_font_scale_new (IMInt scale_factor)
{
    IMAttrInt *attr = (IMAttrInt *) im_attribute_new (IM_TYPE_ATTR_FONT_SCALE);
    _im_assert (attr != 0);
    __im_attr_int_set (attr, scale_factor);
    return (IMAttribute *) attr;
}

IMInt
im_attr_font_scale_get (const IMAttribute *attr)
{
    _im_return_val_if_fail (IM_IS_ATTR_FONT_SCALE (attr), 0);
    return __im_attr_int_get ((const IMAttrInt *) attr);
}

void
im_attr_font_scale_set (IMAttribute *attr, IMInt scale_factor)
{
    _im_return_if_fail (IM_IS_ATTR_FONT_SCALE (attr));
    __im_attr_int_set ((IMAttrInt *) attr, scale_factor);
}

IMAttribute *
im_attr_foreground_new (IMUInt16 red, IMUInt16 green, IMUInt16 blue)
{
    IMAttrColor *attr = (IMAttrColor *) im_attribute_new (IM_TYPE_ATTR_FOREGROUND);
    _im_assert (attr != 0);
    __im_attr_color_set (attr, red, green, blue);
    return (IMAttribute *) attr;
}

void
im_attr_foreground_get (const IMAttribute *attr, IMUInt16 *red, IMUInt16 *green, IMUInt16 *blue)
{
    _im_return_if_fail (IM_IS_ATTR_FOREGROUND (attr));
    __im_attr_color_get ((const IMAttrColor *) attr, red, green, blue);
}

void
im_attr_foreground_set (IMAttribute *attr, IMUInt16 red, IMUInt16 green, IMUInt16 blue)
{
    _im_return_if_fail (IM_IS_ATTR_FOREGROUND (attr));
    __im_attr_color_set ((IMAttrColor *) attr, red, green, blue);
}

IMAttribute *
im_attr_background_new (IMUInt16 red, IMUInt16 green, IMUInt16 blue)
{
    IMAttrColor *attr = (IMAttrColor *) im_attribute_new (IM_TYPE_ATTR_BACKGROUND);
    _im_assert (attr != 0);
    __im_attr_color_set (attr, red, green, blue);
    return (IMAttribute *) attr;
}

void
im_attr_background_get (const IMAttribute *attr, IMUInt16 *red, IMUInt16 *green, IMUInt16 *blue)
{
    _im_return_if_fail (IM_IS_ATTR_BACKGROUND (attr));
    __im_attr_color_get ((const IMAttrColor *) attr, red, green, blue);
}

void
im_attr_background_set (IMAttribute *attr, IMUInt16 red, IMUInt16 green, IMUInt16 blue)
{
    _im_return_if_fail (IM_IS_ATTR_BACKGROUND (attr));
    __im_attr_color_set ((IMAttrColor *) attr, red, green, blue);
}

IMAttribute *
im_attr_underline_new (IMAttrUnderline type)
{
    IMAttrInt *attr = (IMAttrInt *) im_attribute_new (IM_TYPE_ATTR_UNDERLINE);
    _im_assert (attr != 0);
    __im_attr_int_set (attr, type);
    return (IMAttribute *) attr;
}

IMAttrUnderline
im_attr_underline_get (const IMAttribute *attr)
{
    _im_return_val_if_fail (IM_IS_ATTR_UNDERLINE (attr), IM_ATTR_UNDERLINE_NONE);
    return (IMAttrUnderline) __im_attr_int_get ((const IMAttrInt *) attr);
}

void
im_attr_underline_set (IMAttribute *attr, IMAttrUnderline type)
{
    _im_return_if_fail (IM_IS_ATTR_UNDERLINE (attr));
    __im_attr_int_set ((IMAttrInt *) attr, type);
}

IMAttribute *
im_attr_underline_color_new (IMUInt16 red, IMUInt16 green, IMUInt16 blue)
{
    IMAttrColor *attr = (IMAttrColor *) im_attribute_new (IM_TYPE_ATTR_UNDERLINE_COLOR);
    _im_assert (attr != 0);
    __im_attr_color_set (attr, red, green, blue);
    return (IMAttribute *) attr;
}

void
im_attr_underline_color_get (const IMAttribute *attr, IMUInt16 *red, IMUInt16 *green, IMUInt16 *blue)
{
    _im_return_if_fail (IM_IS_ATTR_UNDERLINE_COLOR (attr));
    __im_attr_color_get ((const IMAttrColor *) attr, red, green, blue);
}

void
im_attr_underline_color_set (IMAttribute *attr, IMUInt16 red, IMUInt16 green, IMUInt16 blue)
{
    _im_return_if_fail (IM_IS_ATTR_UNDERLINE_COLOR (attr));
    __im_attr_color_set ((IMAttrColor *) attr, red, green, blue);
}

IMAttribute *
im_attr_strikethrough_new ()
{
    IMAttribute *attr = im_attribute_new (IM_TYPE_ATTR_STRIKETHROUGH);
    _im_assert (attr != 0);
    return attr;
}

IMAttribute *
im_attr_strikethrough_color_new (IMUInt16 red, IMUInt16 green, IMUInt16 blue)
{
    IMAttrColor *attr = (IMAttrColor *) im_attribute_new (IM_TYPE_ATTR_STRIKETHROUGH_COLOR);
    _im_assert (attr != 0);
    __im_attr_color_set (attr, red, green, blue);
    return (IMAttribute *) attr;
}

void
im_attr_strikethrough_color_get (const IMAttribute *attr, IMUInt16 *red, IMUInt16 *green, IMUInt16 *blue)
{
    _im_return_if_fail (IM_IS_ATTR_STRIKETHROUGH_COLOR (attr));
    __im_attr_color_get ((const IMAttrColor *) attr, red, green, blue);
}

void
im_attr_strikethrough_color_set (IMAttribute *attr, IMUInt16 red, IMUInt16 green, IMUInt16 blue)
{
    _im_return_if_fail (IM_IS_ATTR_STRIKETHROUGH_COLOR (attr));
    __im_attr_color_set ((IMAttrColor *) attr, red, green, blue);
}

IMAttribute *
im_attr_highlight_new ()
{
    IMAttribute *attr = im_attribute_new (IM_TYPE_ATTR_HIGHLIGHT);
    _im_assert (attr != 0);
    return attr;
}

IMAttribute *
im_attr_reverse_new ()
{
    IMAttribute *attr = im_attribute_new (IM_TYPE_ATTR_REVERSE);
    _im_assert (attr != 0);
    return attr;
}

IMAttribute *
im_attr_object_new (IMPointer object)
{
    IMAttrObject *attr = (IMAttrObject *) im_attribute_new (IM_TYPE_ATTR_OBJECT);
    _im_assert (attr != 0);
    __im_attr_object_set (attr, object);
    return (IMAttribute *) attr;
}

IMPointer
im_attr_object_get (const IMAttribute *attr)
{
    _im_return_val_if_fail (IM_IS_ATTR_OBJECT (attr), 0);
    return __im_attr_object_get ((const IMAttrObject *) attr);
}

IMPointer
im_attr_object_dup (const IMAttribute *attr)
{
    _im_return_val_if_fail (IM_IS_ATTR_OBJECT (attr), 0);
    return __im_attr_object_dup ((const IMAttrObject *) attr);
}

void
im_attr_object_set (IMAttribute *attr, IMPointer object)
{
    _im_return_if_fail (IM_IS_ATTR_OBJECT (attr));
    __im_attr_object_set ((IMAttrObject *) attr, object);
}

IMAttrList *
im_attr_list_new (void)
{
    IMAttrList *attrlist = (IMAttrList *) im_object_new (IM_TYPE_ATTR_LIST);
    _im_assert (attrlist != 0);
    return attrlist;
}

void
im_attr_list_clear (IMAttrList *list)
{
    _im_return_if_fail (IM_IS_ATTR_LIST (list));
    __im_attr_list_finalize (list);
}

IMBool
im_attr_list_empty (const IMAttrList *list)
{
    _im_return_val_if_fail (IM_IS_ATTR_LIST (list), TRUE);

    return list->attrs == 0;
}

void
im_attr_list_get_range (const IMAttrList *list, IMUInt *start, IMUInt *end)
{
    const IMSList *tmp_list;
    IMUInt min_start = IM_MAXUINT;
    IMUInt max_end   = 0;

    _im_return_if_fail (IM_IS_ATTR_LIST (list));

    /* Do nothing if both start and end are null*/
    if (!start && !end) return;

    tmp_list = list->attrs;
    while (tmp_list) {
        IMAttribute *attr = tmp_list->data;
        if (attr->start < min_start)
            min_start = attr->start;
        if (attr->end > max_end)
            max_end = attr->end;
        tmp_list = tmp_list->next;
    }

    if (start) {
        if (min_start != IM_MAXUINT)
            *start = min_start;
        else
            *start = 0;
    }
    if (end) {
        if (max_end != 0)
            *end = max_end;
        else
            *end = 0;
    }
}

static void
__im_attr_list_insert_internal (IMAttrList *list, IMAttribute *attr, IMBool before)
{
    IMSList *tmp_list;
    IMSList *prev;
    IMSList *link;
    IMUInt   start = attr->start;

    im_object_ref (attr);

    if (!list->attrs) {
        list->attrs = im_slist_prepend (0, attr);
        list->attrs_tail = list->attrs;
    } else if (((IMAttribute*)list->attrs_tail->data)->start < start ||
               (!before && ((IMAttribute*)list->attrs_tail->data)->start == start)) {
        list->attrs_tail = im_slist_append (list->attrs_tail, attr);
        list->attrs_tail = list->attrs_tail->next;
        _im_assert (list->attrs_tail);
    } else {
        prev = 0;
        tmp_list = list->attrs;
        while (tmp_list) {
            if (((IMAttribute*)tmp_list->data)->start > start ||
                (before && ((IMAttribute*)tmp_list->data)->start == start)) {
                link = im_slist_alloc ();
                link->next = tmp_list;
                link->data = attr;

                if (prev)
                    prev->next = link;
                else
                    list->attrs = link;

                break;
            }

            prev = tmp_list;
            tmp_list = tmp_list->next;
        }
    }
}
void
im_attr_list_insert (IMAttrList *list, IMAttribute *attr)
{
    _im_return_if_fail (IM_IS_ATTR_LIST (list));
    _im_return_if_fail (IM_IS_ATTRIBUTE (attr));

    __im_attr_list_insert_internal (list, attr, FALSE);
}

void
im_attr_list_insert_before (IMAttrList *list, IMAttribute *attr)
{
    _im_return_if_fail (IM_IS_ATTR_LIST (list));
    _im_return_if_fail (IM_IS_ATTRIBUTE (attr));

    __im_attr_list_insert_internal (list, attr, TRUE);
}

void
im_attr_list_change (IMAttrList *list, IMAttribute *attr)
{
    IMSList *tmp_list;
    IMSList *prev;
    IMSList *link;
    IMUInt start;
    IMUInt end;

    _im_return_if_fail (IM_IS_ATTR_LIST (list));
    _im_return_if_fail (IM_IS_ATTRIBUTE (attr));

    start = attr->start;
    end   = attr->end;

    im_object_ref (attr);

    /* empty, nothing to do */
    if (start == end) {
        im_object_unref (attr);
        return;
    }

    tmp_list = list->attrs;
    prev = 0;
    while (1) {
        IMAttribute *tmp_attr;

        if (!tmp_list || ((IMAttribute*)tmp_list->data)->start > start) {
            /* We need to insert a new attribute */
            link = im_slist_alloc ();
            link->next = tmp_list;
            link->data = attr;
            if (prev)
                prev->next = link;
            else
                list->attrs = link;

            if (!tmp_list)
                list->attrs_tail = link;

            prev = link;
            tmp_list = prev->next;
            break;
        }

        tmp_attr = tmp_list->data;

        if (IM_ATTRIBUTE_TYPE (tmp_attr) == IM_ATTRIBUTE_TYPE (attr) &&
            tmp_attr->end >= start) {

            /* We overlap with an existing attribute */
            if (im_attribute_equal (tmp_attr, attr)) {
                /* We can merge the new attribute with this attribute */
                if (tmp_attr->end >= end) {
                    /* We are totally overlapping the previous attribute.
                     * No action is needed. */
                    im_object_unref (attr);
                    return;
                }

                tmp_attr->end = end;
                im_object_unref (attr);

                attr = tmp_attr;

                prev = tmp_list;
                tmp_list = tmp_list->next;

                break;
            } else {
                /* Split, truncate, or remove the old attribute */
                if (tmp_attr->end > attr->end) {
                    IMAttribute *end_attr = (IMAttribute *) im_object_clone (tmp_attr);
                    end_attr->start = attr->end;
                    im_attr_list_insert (list, end_attr);
                }

                if (tmp_attr->start == attr->start) {
                    im_object_unref (tmp_attr);
                    tmp_list->data = attr;
                    prev = tmp_list;
                    tmp_list = tmp_list->next;
                    break;
                } else {
                    tmp_attr->end = attr->start;
                }
            }
        }

        prev = tmp_list;
        tmp_list = tmp_list->next;
    }
    /* At this point, prev points to the list node with attr in it,
     * tmp_list points to prev->next. */

    _im_assert (prev->data == attr);
    _im_assert (prev->next == tmp_list);

    /* We now have the range inserted into the list one way or the
     * other. Fix up the remainder */
    while (tmp_list) {
        IMAttribute *tmp_attr = tmp_list->data;

        if (tmp_attr->start > end)
            break;
        else if (IM_ATTRIBUTE_TYPE (tmp_attr) == IM_ATTRIBUTE_TYPE (attr)) {
            if (tmp_attr->end <= attr->end || im_attribute_equal (tmp_attr, attr)) {
                /* We can merge the new attribute with this attribute. */
                attr->end = IM_MAX (end, tmp_attr->end);
                im_object_unref (tmp_attr);
                prev->next = tmp_list->next;

                if (!prev->next)
                    list->attrs_tail = prev;

                im_slist_free_node (tmp_list);
                tmp_list = prev->next;
                continue;
            } else {
                /* Trim the start of this attribute that it begins at the end
                 * of the new attribute. This may involve moving
                 * it in the list to maintain the required non-decreasing
                 * order of start indices */
                IMSList *tmp_list2;
                IMSList *prev2;

                tmp_attr->start = attr->end;

                tmp_list2 = tmp_list->next;
                prev2 = tmp_list;

                while (tmp_list2) {
                    IMAttribute *tmp_attr2 = tmp_list2->data;

                    if (tmp_attr2->start >= tmp_attr->start)
                        break;

                    prev2 = tmp_list2;
                    tmp_list2 = tmp_list2->next;
                }

                /* Now remove and insert before tmp_list2. We'll
                 * hit this attribute again later, but that's harmless. */
                if (prev2 != tmp_list) {
                    IMSList *old_next = tmp_list->next;
                    prev->next = old_next;
                    prev2->next = tmp_list;
                    tmp_list->next = tmp_list2;

                    if (!tmp_list->next)
                        list->attrs_tail = tmp_list;

                    tmp_list = old_next;

                    continue;
                }
            }
        }

        prev = tmp_list;
        tmp_list = tmp_list->next;
    }
}

/* This definition only works when a and b are unsigned; overflow
 * isn't defined in the C standard for signed integers
 */
#define CLAMP_ADD(a,b) (((a) + (b) < (a)) ? IM_MAXUINT : (a) + (b))
void
im_attr_list_splice (IMAttrList *list, IMAttrList *other, IMUInt pos, IMUInt len)
{
    IMSList *tmp_list;

    _im_return_if_fail (IM_IS_ATTR_LIST (list));
    _im_return_if_fail (IM_IS_ATTR_LIST (other));

    im_attr_list_stretch (list, pos, len);

    tmp_list = other->attrs;
    while (tmp_list) {
        IMAttribute *attr = im_object_clone (tmp_list->data);

        attr->start = CLAMP_ADD (attr->start, pos);
        attr->end   = CLAMP_ADD (attr->end, pos);

        /* The attribute could be squashed to zero-length; here
         * im_attr_list_change() will take care of deleting it.  */
        im_attr_list_change (list, attr);

        tmp_list = tmp_list->next;
    }
}

void
im_attr_list_stretch (IMAttrList *list, IMUInt pos, IMUInt len)
{
    IMSList *tmp_list;

    _im_return_if_fail (IM_IS_ATTR_LIST (list));

    tmp_list = list->attrs;
    while (tmp_list) {
        IMAttribute *attr = tmp_list->data;

        if (attr->start <= pos) {
            if (attr->end > pos)
            attr->end = CLAMP_ADD (attr->end, len);
        } else {
            /* This could result in a zero length attribute if it
             * gets squashed up against G_MAXUINT, but deleting such
             * an element could (in theory) suprise the caller, so
             * we don't delete it. */
            attr->start = CLAMP_ADD (attr->start, len);
            attr->end   = CLAMP_ADD (attr->end, len);
        }

        tmp_list = tmp_list->next;
    }
}
#undef CLAMP_ADD

void
im_attr_list_shrink (IMAttrList *list, IMUInt pos, IMUInt len)
{
    IMSList *tmp_list;
    IMSList *prev;
    IMUInt   end = pos + len;

    _im_return_if_fail (IM_IS_ATTR_LIST (list));

    /* Do nothing if remove len is zero */
    if (!len) return;

    tmp_list = list->attrs;
    prev = 0;
    while (tmp_list) {
        IMAttribute *attr = tmp_list->data;

        if (attr->start < pos) {
            if (attr->end > pos)
                /* Remove range interacts with the attribute's
                 * range near head. It will be partially truncated. */
                attr->end -= IM_MIN (len, attr->end - pos);
        } else {
            if (attr->end <= end) {
                /* The attribute falls into remove range completely,
                 * just delete it. */
                im_object_unref (attr);

                if (prev)
                    prev->next = tmp_list->next;
                else
                    list->attrs = tmp_list->next;

                im_slist_free_node (tmp_list);

                if (prev)
                    tmp_list = prev->next;
                else
                    tmp_list = list->attrs;

                if (!tmp_list)
                    list->attrs_tail = prev;

                continue;
            } else if (attr->start < end) {
                /* Remove range interacts with the attribute's
                 * range near tail. It will be partially truncated
                 * and move towards head. */
                attr->start = pos;
                attr->end -= len;
            } else {
                /* Move the attribute left */
                attr->start -= len;
                attr->end -= len;
            }
        }

        prev = tmp_list;
        tmp_list = tmp_list->next;
    }

    /* TODO: Some identical attributes may become conterminous,
     * they should be merged. */
}

void
im_attr_list_clear_range (IMAttrList *list, IMUInt pos, IMUInt len)
{
    IMSList *tmp_list;
    IMSList *prev;
    IMUInt   end = pos + len;

    _im_return_if_fail (IM_IS_ATTR_LIST (list));

    /* Do nothing if remove len is zero */
    if (!len) return;

    tmp_list = list->attrs;
    prev = 0;
    while (tmp_list) {
        IMAttribute *attr = tmp_list->data;

        if (attr->start < pos) {
            if (attr->end > pos && attr->end <= end) {
                /* Remove range interacts with the attribute's
                 * range in the head. The attr will be truncated. */
                attr->end = pos;
            } else if (attr->end > end) {
                /* Remove range interacts with the attribute's
                 * range in the middle. The attr will be splitted into two. */
                IMAttribute *new_attr = (IMAttribute*) im_object_clone (attr);
                attr->end = pos;
                new_attr->start = end;
                im_attr_list_insert (list, new_attr);
            }
        } else {
            if (attr->end <= end) {
                /* The attribute falls into remove range completely,
                 * just delete it. */
                im_object_unref (attr);

                if (prev)
                    prev->next = tmp_list->next;
                else
                    list->attrs = tmp_list->next;

                im_slist_free_node (tmp_list);

                if (prev)
                    tmp_list = prev->next;
                else
                    tmp_list = list->attrs;

                if (!tmp_list)
                    list->attrs_tail = prev;

                continue;
            } else if (attr->start < end) {
                /* Remove range interacts with the attribute's
                 * range in the tail. It will be partially truncated
                 * */
                attr->start = end;
            }
            /* Other attributes are not affected. */
        }

        prev = tmp_list;
        tmp_list = tmp_list->next;
    }
}

void
im_attr_list_remove (IMAttrList *list, IMUInt pos, IMUInt len, IMType attrtype)
{
    IMSList *tmp_list;
    IMSList *prev;
    IMUInt   end = pos + len;

    _im_return_if_fail (IM_IS_ATTR_LIST (list));

    /* Do nothing if remove len is zero */
    if (!len) return;

    tmp_list = list->attrs;
    prev = 0;
    while (tmp_list) {
        IMAttribute *attr = tmp_list->data;

        if (IM_ATTRIBUTE_TYPE (attr) == attrtype) {
            if (attr->start < pos) {
                if (attr->end > pos && attr->end <= end) {
                    /* Remove range interacts with the attribute's
                     * range in the head. The attr will be truncated. */
                    attr->end = pos;
                } else if (attr->end > end) {
                    /* Remove range interacts with the attribute's
                     * range in the middle. The attr will be splitted into two. */
                    IMAttribute *new_attr = (IMAttribute*) im_object_clone (attr);
                    attr->end = pos;
                    new_attr->start = end;
                    im_attr_list_insert (list, new_attr);
                }
            } else {
                if (attr->end <= end) {
                    /* The attribute falls into remove range completely,
                     * just delete it. */
                    im_object_unref (attr);

                    if (prev)
                        prev->next = tmp_list->next;
                    else
                        list->attrs = tmp_list->next;

                    im_slist_free_node (tmp_list);

                    if (prev)
                        tmp_list = prev->next;
                    else
                        tmp_list = list->attrs;

                    if (!tmp_list)
                        list->attrs_tail = prev;

                    continue;
                } else if (attr->start < end) {
                    /* Remove range interacts with the attribute's
                     * range in the tail. It will be partially truncated
                     * */
                    attr->start = end;
                }
                /* Other attributes are not affected. */
            }
        }

        prev = tmp_list;
        tmp_list = tmp_list->next;
    }
}

IMAttrList *
im_attr_list_get_sub_list (const IMAttrList *list, IMUInt pos, IMUInt len)
{
    const IMSList   *tmp_list;
    IMAttrList      *new_list;
    IMUInt           end = pos + len;

    _im_return_val_if_fail (IM_IS_ATTR_LIST (list), 0);

    new_list = im_attr_list_new ();

    /* Do nothing if len is zero */
    if (!len)
        return new_list;

    tmp_list = list->attrs;
    while (tmp_list) {
        const IMAttribute *attr = tmp_list->data;

        if ((attr->start >= pos && attr->start < end) ||
            (attr->end > pos && attr->end <= end) ||
            (attr->start <= pos && attr->end >= end)) {
            /* The attribute falls into the range, copy it,
             * and adjust its range accordingly. */
            IMAttribute *new_attr = im_object_clone (attr);

            new_attr->start -= IM_MIN(pos, new_attr->start);
            new_attr->end   -= pos;

            if (new_attr->end > len)
                new_attr->end = len;

            im_attr_list_insert (new_list, new_attr);
        }

        tmp_list = tmp_list->next;
    }

    return new_list;
}

void
im_attr_list_foreach (const IMAttrList *list, IMFunc func, IMPointer user_data)
{
    const IMSList   *tmp_list;

    _im_return_if_fail (IM_IS_ATTR_LIST (list));
    _im_return_if_fail (func != 0);

    tmp_list = list->attrs;
    while (tmp_list) {
        (*func)(tmp_list->data, user_data);
        tmp_list = tmp_list->next;
    }
}

void
im_attr_list_optimize (IMAttrList *list)
{
    IMSList    *tmp_list;
    IMSList    *prev;

    _im_return_if_fail (IM_IS_ATTR_LIST (list));

    /* Just rebuild the list by reinserting each attribute
     * from tail to head by im_attr_list_change(). */

    tmp_list = im_slist_reverse (list->attrs);
    prev = 0;

    list->attrs = 0;
    list->attrs_tail = 0;

    while (tmp_list) {
        im_attr_list_change (list, tmp_list->data);

        im_object_unref (tmp_list->data);

        prev = tmp_list;
        tmp_list = tmp_list->next;

        im_slist_free_node (prev);
    }
}

void
im_attr_iterator_remove (IMAttrIterator *iterator)
{
    IMSList *next;

    _im_return_if_fail (iterator != 0);
    _im_return_if_fail (IM_IS_ATTR_LIST (iterator->list));

    /* Invalid iterator, just return */
    if (!iterator->node)
        return;

    next = iterator->node->next;

    if (!next) iterator->list->attrs_tail = iterator->prev;

    if (!iterator->prev)
        iterator->list->attrs = next;
    else
        iterator->prev->next = next;

    im_object_unref (iterator->node->data);
    im_slist_free_node (iterator->node);

    iterator->node = next;
}

IMAttrIterator *
im_attr_list_get_iterator (IMAttrList *list)
{
    IMAttrIterator *it;

    _im_return_val_if_fail (IM_IS_ATTR_LIST (list), 0);

    it = im_slice_new (IMAttrIterator);

    _im_assert (it != 0);

    it->list = list;
    it->prev = 0;
    it->node = list->attrs;

    return it;
}

IMAttrIterator *
im_attr_iterator_clone (const IMAttrIterator *iterator)
{
    IMAttrIterator *new_it;

    _im_return_val_if_fail (iterator != 0, 0);

    new_it = im_slice_new (IMAttrIterator);
    _im_assert (new_it != 0);

    *new_it = *iterator;

    return new_it;
}

void
im_attr_iterator_destroy (IMAttrIterator *iterator)
{
    _im_return_if_fail (iterator != 0);
    im_slice_delete (IMAttrIterator, iterator);
}

IMBool
im_attr_iterator_next (IMAttrIterator *iterator)
{
    _im_return_val_if_fail (iterator != 0, FALSE);
    _im_return_val_if_fail (IM_IS_ATTR_LIST (iterator->list), FALSE);

    if (iterator->node && iterator->node->next) {
        iterator->prev = iterator->node;
        iterator->node = iterator->node->next;
        return TRUE;
    }

    return FALSE;
}

IMAttribute *
im_attr_iterator_get (const IMAttrIterator *iterator)
{
    _im_return_val_if_fail (iterator != 0, 0);
    _im_return_val_if_fail (iterator->node != 0, 0);

    return iterator->node->data;
}

IMBool
im_attr_iterator_valid (const IMAttrIterator *iterator)
{
    return iterator && iterator->list && iterator->node;
}

IMBool
im_attr_iterator_equal (const IMAttrIterator *iterator1, const IMAttrIterator *iterator2)
{
    _im_return_val_if_fail (iterator1 != 0, FALSE);
    _im_return_val_if_fail (iterator2 != 0, FALSE);

    return iterator1->list == iterator2->list && iterator1->node == iterator2->node;
}

/*
vi:ts=4:nowrap:ai:expandtab
*/
